/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.test.core.utils

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.core.type.TypeReference
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import tony.core.enums.DefaultAndAsNull
import tony.core.enums.IntEnumValue
import tony.core.enums.getEnum
import tony.core.utils.convertTo
import tony.core.utils.getFromRootAsString
import tony.core.utils.jsonNode
import tony.core.utils.jsonToObj
import tony.core.utils.toJsonString
import java.io.ByteArrayInputStream
import java.math.BigDecimal
import java.time.LocalDateTime
import kotlin.jvm.java

/**
 * Jsons工具类测试
 *
 * 测试JSON序列化和反序列化相关功能
 * @author tangli
 * @date 2025/08/26 09:38
 */
@DisplayName("Jsons测试")
class JsonsTest {

    // 测试用枚举
    enum class Gender(override val value: Int) : IntEnumValue {
        FEMALE(0),
        MALE(1),
        @DefaultAndAsNull
        UNKNOWN(-1);
        companion object  {
            @JvmStatic
            @JsonCreator
            fun create(value: Int): Gender? {
                return Gender::class.java.getEnum(value)
            }
        }
    }

    /**
     * 测试数据类
     */
    data class TestUser(
        val id: Long,
        val name: String,
        val email: String?,
        val age: Int,
        val balance: BigDecimal,
        val createTime: LocalDateTime?,
        val gender: Gender?
    )

    /**
     * 复杂测试数据类
     */
    data class TestOrder(
        val orderId: String,
        val user: TestUser,
        val items: List<TestItem>,
        val metadata: Map<String, Any?>
    )

    /**
     * 测试商品类
     */
    data class TestItem(
        val itemId: String,
        val name: String,
        val price: BigDecimal,
        val quantity: Int
    )

    @Nested
    @DisplayName("Jsons.toJsonString()测试")
    inner class ToJsonStringTest {
        @Test
        @DisplayName("Jsons.toJsonString():对象转JSON字符串")
        fun testToJsonString() {
            val user = TestUser(
                id = 1L,
                name = "张三",
                email = "zhangsan@example.com",
                age = 25,
                balance = BigDecimal("1000.50"),
                createTime = null,
                gender = Gender.MALE
            )
            val json = user.toJsonString()
            assertNotNull(json)
            assertTrue(json.contains("\"id\":\"1\""))
            assertTrue(json.contains("\"gender\":1"))
            assertTrue(json.contains("\"name\":\"张三\""))
            assertTrue(json.contains("\"email\":\"zhangsan@example.com\""))
        }

        @Test
        @DisplayName("Jsons.toJsonString():null对象")
        fun testToJsonStringWithNull() {
            val nullUser: TestUser? = null
            val json = nullUser.toJsonString()
            assertEquals("", json)
        }

        @Test
        @DisplayName("Jsons.toJsonString():复杂对象")
        fun testToJsonStringWithComplexObject() {
            val user = TestUser(
                id = 1L,
                name = "张三",
                email = "zhangsan@example.com",
                age = 25,
                balance = BigDecimal("1000.50"),
                createTime = null,
                gender = Gender.MALE
            )
            val items = listOf(
                TestItem("item1", "商品1", BigDecimal("100.00"), 2),
                TestItem("item2", "商品2", BigDecimal("200.00"), 1)
            )
            val order = TestOrder(
                orderId = "order123",
                user = user,
                items = items,
                metadata = mapOf("source" to "web", "discount" to null)
            )
            val json = order.toJsonString()
            assertNotNull(json)
            assertTrue(json.contains("\"orderId\":\"order123\""))
            assertTrue(json.contains("\"gender\":1"))
            assertTrue(json.contains("\"user\":"))
            assertTrue(json.contains("\"items\":"))
        }
    }

    @Nested
    @DisplayName("Jsons.jsonToObj()测试")
    inner class JsonToObjTest {
        @Test
        @DisplayName("Jsons.jsonToObj():JSON字符串转对象(泛型)")
        fun testJsonToObjWithReified() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null,"gender":0}"""
            val user = json.jsonToObj<TestUser>()
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
            assertEquals("zhangsan@example.com", user.email)
            assertEquals(25, user.age)
            assertEquals(BigDecimal("1000.50"), user.balance)
            assertEquals(Gender.FEMALE, user.gender)
            assertNull(user.createTime)
        }

        @Test
        @DisplayName("Jsons.jsonToObj():JSON字符串转对象(Class)")
        fun testJsonToObjWithClass() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val user = json.jsonToObj(TestUser::class.java)
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }

        @Test
        @DisplayName("Jsons.jsonToObj():JSON字符串转对象(TypeReference)")
        fun testJsonToObjWithTypeReference() {
            val json = """[{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}]"""
            val users = json.jsonToObj(object : TypeReference<List<TestUser>>() {})
            assertNotNull(users)
            assertEquals(1, users.size)
            assertEquals("张三", users[0].name)
        }

        @Test
        @DisplayName("Jsons.jsonToObj():JSON字符串转Map")
        fun testJsonToObjToMap() {
            val json = """{"name":"张三","age":25,"active":true}"""
            val map = json.jsonToObj<Map<String, Any>>()
            assertNotNull(map)
            assertEquals("张三", map["name"])
            assertEquals(25, map["age"])
            assertEquals(true, map["active"])
        }

        @Test
        @DisplayName("Jsons.jsonToObj():无效JSON字符串")
        fun testJsonToObjWithInvalidJson() {
            val invalidJson = "{invalid json}"
            assertThrows(Exception::class.java) {
                invalidJson.jsonToObj<TestUser>()
            }
        }
    }

    @Nested
    @DisplayName("Jsons.jsonNode()测试")
    inner class JsonNodeTest {
        @Test
        @DisplayName("Jsons.jsonNode():字符串转JsonNode")
        fun testJsonNodeFromString() {
            val json = """{"name":"张三","age":25}"""
            val node = json.jsonNode()
            assertNotNull(node)
            assertEquals("张三", node.get("name").asText())
            assertEquals(25, node.get("age").asInt())
        }

        @Test
        @DisplayName("Jsons.jsonNode():字节数组转JsonNode")
        fun testJsonNodeFromByteArray() {
            val json = """{"name":"张三","age":25}"""
            val bytes = json.toByteArray()
            val node = bytes.jsonNode()
            assertNotNull(node)
            assertEquals("张三", node.get("name").asText())
            assertEquals(25, node.get("age").asInt())
        }

        @Test
        @DisplayName("Jsons.jsonNode():输入流转JsonNode")
        fun testJsonNodeFromInputStream() {
            val json = """{"name":"张三","age":25}"""
            val inputStream = ByteArrayInputStream(json.toByteArray())
            val node = inputStream.jsonNode()
            assertNotNull(node)
            assertEquals("张三", node.get("name").asText())
            assertEquals(25, node.get("age").asInt())
        }

        @Test
        @DisplayName("Jsons.jsonNode():数组JSON")
        fun testJsonNodeWithArray() {
            val json = """[{"name":"张三"},{"name":"李四"}]"""
            val node = json.jsonNode()
            assertNotNull(node)
            assertTrue(node.isArray)
            assertEquals(2, node.size())
            assertEquals("张三", node.get(0).get("name").asText())
            assertEquals("李四", node.get(1).get("name").asText())
        }
    }

    @Nested
    @DisplayName("Jsons.convertTo()测试")
    inner class ConvertToTest {
        @Test
        @DisplayName("Jsons.convertTo():JsonNode转对象(泛型)")
        fun testConvertToWithReified() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val node = json.jsonNode()
            val user = node.convertTo<TestUser>()
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }

        @Test
        @DisplayName("Jsons.convertTo():JsonNode转对象(Class)")
        fun testConvertToWithClass() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val node = json.jsonNode()
            val user = node.convertTo(TestUser::class.java)
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }

        @Test
        @DisplayName("Jsons.convertTo():JsonNode转对象(TypeReference)")
        fun testConvertToWithTypeReference() {
            val json = """[{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}]"""
            val node = json.jsonNode()
            val users = node.convertTo(object : TypeReference<List<TestUser>>() {})
            assertNotNull(users)
            assertEquals(1, users.size)
            assertEquals("张三", users[0].name)
        }
    }

    @Nested
    @DisplayName("Jsons.getFromRootAsString()测试")
    inner class GetFromRootAsStringTest {
        @Test
        @DisplayName("Jsons.getFromRootAsString():获取根节点字段值")
        fun testGetFromRootAsString() {
            val json = """{"name":"张三","age":25,"email":"zhangsan@example.com"}"""
            val name = json.getFromRootAsString("name")
            val age = json.getFromRootAsString("age")
            val email = json.getFromRootAsString("email")

            assertEquals("张三", name)
            assertEquals("25", age)
            assertEquals("zhangsan@example.com", email)
        }

        @Test
        @DisplayName("Jsons.getFromRootAsString():获取不存在的字段")
        fun testGetFromRootAsStringWithNonExistentField() {
            val json = """{"name":"张三","age":25}"""
            val result = json.getFromRootAsString("nonexistent")
            assertNull(result)
        }

        @Test
        @DisplayName("Jsons.getFromRootAsString():获取null字段")
        fun testGetFromRootAsStringWithNullField() {
            val json = """{"name":"张三","email":null}"""
            val result = json.getFromRootAsString("email")
            assertNull(result)
        }

    }

    @Nested
    @DisplayName("Jsons.ByteArray.jsonToObj()测试")
    inner class ByteArrayJsonToObjTest {
        @Test
        @DisplayName("Jsons.ByteArray.jsonToObj():字节数组转对象(泛型)")
        fun testByteArrayJsonToObjWithReified() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val bytes = json.toByteArray()
            val user = bytes.jsonToObj<TestUser>()
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }

        @Test
        @DisplayName("Jsons.ByteArray.jsonToObj():字节数组转对象(Class)")
        fun testByteArrayJsonToObjWithClass() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val bytes = json.toByteArray()
            val user = bytes.jsonToObj(TestUser::class.java)
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }
    }

    @Nested
    @DisplayName("Jsons.InputStream.jsonToObj()测试")
    inner class InputStreamJsonToObjTest {
        @Test
        @DisplayName("Jsons.InputStream.jsonToObj():输入流转对象(泛型)")
        fun testInputStreamJsonToObjWithReified() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val inputStream = ByteArrayInputStream(json.toByteArray())
            val user = inputStream.jsonToObj<TestUser>()
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }

        @Test
        @DisplayName("Jsons.InputStream.jsonToObj():输入流转对象(Class)")
        fun testInputStreamJsonToObjWithClass() {
            val json = """{"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"balance":1000.50,"createTime":null}"""
            val inputStream = ByteArrayInputStream(json.toByteArray())
            val user = inputStream.jsonToObj(TestUser::class.java)
            assertNotNull(user)
            assertEquals(1L, user.id)
            assertEquals("张三", user.name)
        }
    }

    @Nested
    @DisplayName("Jsons集成测试")
    inner class IntegrationTest {
        @Test
        @DisplayName("Jsons集成测试:完整序列化反序列化流程")
        fun testCompleteSerializationDeserialization() {
            // 创建复杂对象
            val user = TestUser(
                id = 1L,
                name = "张三",
                email = "zhangsan@example.com",
                age = 25,
                balance = BigDecimal("1000.50"),
                createTime = null,
                gender = Gender.FEMALE,
            )
            val items = listOf(
                TestItem("item1", "商品1", BigDecimal("100.00"), 2),
                TestItem("item2", "商品2", BigDecimal("200.00"), 1)
            )
            val originalOrder = TestOrder(
                orderId = "order123",
                user = user,
                items = items,
                metadata = mapOf("source" to "web", "discount" to 10.5)
            )

            // 序列化
            val json = originalOrder.toJsonString()
            assertNotNull(json)

            // 反序列化
            val deserializedOrder = json.jsonToObj<TestOrder>()
            assertNotNull(deserializedOrder)

            // 验证数据完整性
            assertEquals(originalOrder.orderId, deserializedOrder.orderId)
            assertEquals(originalOrder.user.id, deserializedOrder.user.id)
            assertEquals(originalOrder.user.name, deserializedOrder.user.name)
            assertEquals(originalOrder.user.gender, deserializedOrder.user.gender)
            assertEquals(originalOrder.items.size, deserializedOrder.items.size)
            assertEquals(originalOrder.items[0].itemId, deserializedOrder.items[0].itemId)
            assertEquals(originalOrder.metadata["source"], deserializedOrder.metadata["source"])
        }

        @Test
        @DisplayName("Jsons集成测试:JsonNode中间转换")
        fun testJsonNodeIntermediateConversion() {
            val user = TestUser(
                id = 1L,
                name = "张三",
                email = "zhangsan@example.com",
                age = 25,
                balance = BigDecimal("1000.50"),
                createTime = null,
                gender = Gender.MALE,
            )

            // 对象 -> JSON字符串 -> JsonNode -> 对象
            val json = user.toJsonString()
            val node = json.jsonNode()
            val convertedUser = node.convertTo<TestUser>()

            assertEquals(user.id, convertedUser.id)
            assertEquals(user.name, convertedUser.name)
            assertEquals(user.email, convertedUser.email)
            assertEquals(user.age, convertedUser.age)
            assertEquals(user.balance, convertedUser.balance)
            assertEquals(user.gender, convertedUser.gender)
        }
    }
}
